package com.innovation.ic.sc.base.service.sc.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.innovation.ic.sc.base.mapper.sc.RoleMapper;
import com.innovation.ic.sc.base.model.sc.MapUserRole;
import com.innovation.ic.sc.base.model.sc.MenuTree;
import com.innovation.ic.sc.base.model.sc.Role;
import com.innovation.ic.sc.base.model.sc.User;
import com.innovation.ic.sc.base.pojo.constant.handler.RedisStorage;
import com.innovation.ic.sc.base.pojo.constant.model.MenuSelected;
import com.innovation.ic.sc.base.pojo.constant.model.RoleAvailableStatus;
import com.innovation.ic.sc.base.pojo.enums.RedisKeyPrefixEnum;
import com.innovation.ic.sc.base.pojo.variable.PageNationPojo;
import com.innovation.ic.sc.base.pojo.variable.RedisRolePojo;
import com.innovation.ic.sc.base.pojo.variable.ServiceResult;
import com.innovation.ic.sc.base.pojo.variable.menu.Menu;
import com.innovation.ic.sc.base.pojo.variable.menu.RolePojo;
import com.innovation.ic.sc.base.service.ServiceHelper;
import com.innovation.ic.sc.base.service.sc.RoleService;
import com.innovation.ic.sc.base.vo.menu.RoleVo;
import org.bson.types.ObjectId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * RoleService的具体实现类
 */
@Service
@Transactional
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
    private static final Logger log = LoggerFactory.getLogger(RoleServiceImpl.class);
    @Resource
    private ServiceHelper serviceHelper;

    /**
     * 查找所有角色
     *
     * @return
     */
    @Override
    public ServiceResult<List<Role>> findAllByUserId(String userId) {
        ServiceResult<List<Role>> serviceResult = new ServiceResult<List<Role>>();
        Query query = new Query(Criteria.where("creatorId").is(userId));
        List<Role> roleList = serviceHelper.getMongodbManager().find(query, Role.class);
        serviceResult.setMessage(ServiceResult.SELECT_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        serviceResult.setResult(roleList);
        return serviceResult;
    }

    /**
     * 根据id，改变角色的状态
     *
     * @param id
     * @return
     */
    @Override
    public ServiceResult<Boolean> changeAvailableStatus(String id, Integer status) {
        ServiceResult<Boolean> serviceResult = new ServiceResult<Boolean>();
        //只有停用才需要校验
        if (status.equals(RoleAvailableStatus.NO)) {
            if (checkDeactivate(id)) {
                serviceResult.setMessage("当前角色正在被账号引用，无法停用");
                serviceResult.setSuccess(Boolean.FALSE);
                return serviceResult;
            }
        }

        // 通过query根据id查询出对应对象，通过update对象进行修改
        Query query = new Query(Criteria.where("_id").is(new ObjectId(id)));
        Update update = new Update()
                .set("available", status)
                .set("updateTime", new Date());
        serviceHelper.getMongodbManager().updateFirst(query, update, Role.class);
        //清空缓存中历史数据
        String redisRoleKey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + id;
        serviceHelper.getRedisManager().del(redisRoleKey);

        //禁用需要更改所有角色信息
        updateUserMenu(id);
        serviceResult.setMessage(ServiceResult.UPDATE_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        return serviceResult;
    }

    /**
     * 检验是否能停用
     *
     * @param id
     * @return
     */
    public Boolean checkDeactivate(String id) {
        //通过角色id 获取对应的角色信息
        Query query = Query.query(Criteria.where("roleId").is(id));
        List<MapUserRole> mapUserRoles = serviceHelper.getMongodbManager().find(query, MapUserRole.class);
        for (MapUserRole mapUserRole : mapUserRoles) {
            Query queryMapUserRole = Query.query(Criteria.where("userId").is(mapUserRole.getUserId()));
            List<MapUserRole> mapUserRoleList = serviceHelper.getMongodbManager().find(queryMapUserRole, MapUserRole.class);
            Integer count = mapUserRoleList.size();
            for (MapUserRole userRole : mapUserRoleList) {
                if (!userRole.getRoleId().equals(id)) {
                    Query queryId = Query.query(Criteria.where("_id").is(userRole.getRoleId()));
                    Role role = serviceHelper.getMongodbManager().findOne(queryId, Role.class);
                    if (null == role) {
                        continue;
                    }
                    if (role.getAvailable().equals(RoleAvailableStatus.NO)) {
                        count--;
                    }
                }
            }

            //只要有人员 ,当前停用角色是唯一的时候 不能停用
            if (count == 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * 插入Role对象和对应的Menu对象
     *
     * @param role
     * @return
     */
    @Override
    public ServiceResult<String> saveRole(Role role) {
        ServiceResult<String> serviceResult = new ServiceResult<String>();

        Role result = serviceHelper.getMongodbManager().insert(role);

        serviceResult.setMessage(ServiceResult.INSERT_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        serviceResult.setResult(result.get_id().toString());
        return serviceResult;
    }

    /**
     * 主账号修改角色名称和对应的菜单权限时，返回菜单树形目录（不包括账号管理和角色管理），其中有权限的菜单项被选中
     *
     * @param role
     * @return
     */
    @Override
    public ServiceResult<Role> showMenuForUpdateRole(RoleVo role) {
        ServiceResult<Role> serviceResult = new ServiceResult<Role>();

        Query query = new Query(Criteria.where("_id").is(role.get_id()));
        Update update = new Update()
                .set("name", role.getName())
                .set("menuList", role.getMenuVoList())
                .set("updateTime", new Date());
        serviceHelper.getMongodbManager().updateFirst(query, update, Role.class);
        //清空缓存中历史数据
        String redisRoleKey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + role.get_id();
        serviceHelper.getRedisManager().del(redisRoleKey);

        serviceResult.setMessage(ServiceResult.SELECT_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        Role role1 = serviceHelper.getMongodbManager().findOne(query, Role.class);
        updateUserMenu(role1.get_id().toString());
        serviceResult.setResult(role1);
        return serviceResult;
    }

    public void updateUserMenu(String roleId) {
        //通过角色id 获取对应的角色信息
        Query query = Query.query(Criteria.where("roleId").is(roleId));
        List<MapUserRole> mapUserRoles = serviceHelper.getMongodbManager().find(query, MapUserRole.class);
        //确定那些用户拥有修改的权限
        for (MapUserRole mapUserRole : mapUserRoles) {
            //查询用户是否是经理
            User user = serviceHelper.getUserMapper().selectById(mapUserRole.getUserId());

            List<Menu> menuList = serviceHelper.getMenuHandler().createSubAccountManagerMenuTree().getFirstMenuList();
            Query queryMapUserRole = Query.query(Criteria.where("userId").is(mapUserRole.getUserId()));
            List<MapUserRole> mapUserRoleList = serviceHelper.getMongodbManager().find(queryMapUserRole, MapUserRole.class);
            //查询用户拥有的角色信息
            for (MapUserRole userRole : mapUserRoleList) {
                Query roleQuery = Query.query(Criteria.where("_id").is(userRole.getRoleId()));
                Role role = serviceHelper.getMongodbManager().findOne(roleQuery, Role.class);
                //只有是启用状态的角色才加入
                if (role.getAvailable().equals(RoleAvailableStatus.YES)) {
                    if (user == null) {
                        menuList = setMenu(menuList, role.getMenuList(), 0);
                    } else {
                        menuList = setMenu(menuList, role.getMenuList(), user.getPosition());
                    }
                }
            }
            MenuTree menuTree = new MenuTree();
            menuTree.setUserId(mapUserRole.getUserId());
            menuTree.setCreateTime(new Date());
            menuTree.setFirstMenuList(menuList);
            // 删除
            Query queryMenuTree = Query.query(Criteria.where("userId").is(menuTree.getUserId()));
            serviceHelper.getMongodbManager().remove(queryMenuTree, MenuTree.class);
            // 添加
            serviceHelper.getMongodbManager().insert(menuTree);
        }
    }

    /**
     * 设置菜单
     *
     * @param newMenuList
     * @param menuList
     * @param isManager
     * @return
     */
    public List<Menu> setMenu(List<Menu> newMenuList, List<Menu> menuList, Integer isManager) {
        for (Menu menu : newMenuList) {
            //设置经理默认
            if (menu.getId().equals("6-1") && isManager == 1) {
                menu.setSelected(MenuSelected.YES);
                continue;
            }
            for (Menu menu1 : menuList) {
                if (menu.getId().equals(menu1.getId())) {
                    //设置勾选
                    if (menu1.getSelected().equals(MenuSelected.YES)) {
                        menu.setSelected(menu1.getSelected());
                    }
                    setMenu(menu.getChildMenuList(), menu1.getChildMenuList(), isManager);
                    continue;
                }
            }
        }
        return newMenuList;
    }

    @Override
    public ServiceResult<Role> showMenuForRoleById(String roleId) {
        ServiceResult<Role> serviceResult = new ServiceResult<Role>();

        Query query = new Query(Criteria.where("_id").is(new ObjectId(roleId)));
        Role role = serviceHelper.getMongodbManager().findOne(query, Role.class);

        serviceResult.setMessage(ServiceResult.SELECT_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        serviceResult.setResult(role);
        return serviceResult;
    }

    /**
     * 根据name查找角色
     *
     * @param name
     * @return
     */
    @Override
    public ServiceResult<Role> findByName(String name, String creatorId) {
        ServiceResult<Role> serviceResult = new ServiceResult<Role>();
        Criteria criteria = new Criteria();
        criteria.and("name").is(name);
        criteria.and("creatorId").is(creatorId);
        Query query = new Query(criteria);
        Role role = serviceHelper.getMongodbManager().findOne(query, Role.class);

        serviceResult.setMessage(ServiceResult.SELECT_SUCCESS);
        serviceResult.setSuccess(Boolean.TRUE);
        serviceResult.setResult(role);
        return serviceResult;
    }

    /**
     * 分页查询角色信息
     *
     * @param userMainId
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public PageNationPojo<RolePojo> pageFindAllByUserId(String userMainId, Integer pageNo, Integer pageSize) {
        //通过query构建查询
        Query query = new Query(Criteria.where("creatorId").is(userMainId));

        //查询总条数
        long total = serviceHelper.getMongodbManager().count(query, Role.class);

        Sort sort = Sort.by(Sort.Direction.DESC, "createTime");
        //分页
        Pageable pageable = PageRequest.of(pageNo - 1, pageSize, sort);
        query.with(pageable);

        List<Role> roleList = serviceHelper.getMongodbManager().find(query, Role.class);
        List<RolePojo> rolePojoList = roleList.stream().map(role -> {
            RolePojo rolePojo = new RolePojo();
            BeanUtils.copyProperties(role, rolePojo);
            //需要转换id前端显示错误
            rolePojo.set_id(role.get_id().toString());
            return rolePojo;
        }).collect(Collectors.toList());
        Page<RolePojo> rolePage = new PageImpl(rolePojoList, pageable, total);
        return new PageNationPojo(rolePage.getNumber() + 1, rolePage.getSize(), rolePage.getTotalElements(), rolePage.getTotalPages(), rolePage.getContent());
    }

    /**
     * 进行用户(子账号)与角色关联
     *
     * @param roleListId
     * @param id
     * @return
     */
    @Override
    public void saveUserToRole(List<String> roleListId, String id, String mainId, Integer isManager) {
        //通过角色id 获取对应的角色信息（从缓存中取值）
        List<Role> roles = this.selectRedisData(roleListId);
        if (roles.size() != 0) {
            List<MapUserRole> mapUserRoles = new ArrayList<>();
            for (Role role : roles) {
                MapUserRole mapUserRole = new MapUserRole();
                mapUserRole.setRoleId(String.valueOf(role.get_id()));
                mapUserRole.setUserId(id);
                mapUserRoles.add(mapUserRole);
            }
            // 删除
            Query query = Query.query(Criteria.where("userId").is(mapUserRoles.get(0).getUserId()));
            serviceHelper.getMongodbManager().remove(query, MapUserRole.class);
            // 添加 用户与角色表
            serviceHelper.getMongodbManager().bulkOpsInsert(MapUserRole.class, mapUserRoles);

            //初始菜单
            List<Menu> menuList = serviceHelper.getMenuHandler().createSubAccountManagerMenuTree().getFirstMenuList();
            for (Role menuData : roles) {
                setMenu(menuList, menuData.getMenuList(), isManager);
            }

            MenuTree menuTree = new MenuTree();
            //用户id
            menuTree.setUserId(id);
            //主账号id
            menuTree.setCreatorId(mainId);
            menuTree.setCreateTime(new Date());
            menuTree.setFirstMenuList(menuList);
            // 删除
            Query queryMenuTree = Query.query(Criteria.where("userId").is(menuTree.getUserId()));
            serviceHelper.getMongodbManager().remove(queryMenuTree, MenuTree.class);
            // 添加菜单关系
            serviceHelper.getMongodbManager().insert(menuTree);
            //清空redis缓存 人员管理前缀
            List<String> list = deleteRedisUserManagement(mainId);
            serviceHelper.getRedisManager().del(list);//进行删除
        } else {
            log.info("没有对应角色，无法与角色关联");
        }
    }

    private List<String> deleteRedisUserManagement(String id) {//通过主账号id 去清空人员管理数据redis中缓存
        List<String> list = new ArrayList<>();//用于存放主账号与子账号id并拼接部门id
        User user = serviceHelper.getUserMapper().selectById(id);
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("father_id", user.getId());//需要查询主账号下的所有子账号id
        List<User> users = serviceHelper.getUserMapper().selectList(userQueryWrapper);//所有子账号id
        for (User user1 : users) {
            String keyId = RedisKeyPrefixEnum.USER_MANAGEMENT_DATA.getCode() + "_" + user1.getId();//子账号拼redis对应前缀
            list.add(keyId);//子账号id存入
        }
        list.add(RedisKeyPrefixEnum.USER_MANAGEMENT_DATA.getCode() + "_" + user.getId());//主账号拼redis对应前缀
        return list;
    }

    /**
     * 初始化角色所有数据
     *
     * @return
     */
    @Override
    public void initRoleData() {
        // 删除redis中的主键相关数据
        Boolean deleteResult = serviceHelper.getRedisManager().delRedisDataByKeyPrefix(RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "*");
        if (deleteResult) {
            log.info("删除[{}]开头的数据成功", RedisKeyPrefixEnum.ROLE_ID_DATA.getCode());
        }

        //获取所有角色信息存入redis缓存
        List<Role> roles = serviceHelper.getMongodbManager().findAll(Role.class);

        for (Role role : roles) {
            if (role.get_id() != null) {
                RedisRolePojo redisRolePojo = this.packaRedisRoleData(role);
                String redisKey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + redisRolePojo.get_id();
                String apiResultJson = JSONObject.toJSONString(redisRolePojo, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue);
                serviceHelper.getRedisManager().set(redisKey, apiResultJson);
            }
        }
    }


    private RedisRolePojo packaRedisRoleData(Role role) {  //封装统一对象存入redis
        RedisRolePojo redisRolePojo = new RedisRolePojo();
        redisRolePojo.set_id(String.valueOf(role.get_id()));
        redisRolePojo.setAvailable(role.getAvailable());
        redisRolePojo.setCreateTime(role.getCreateTime());
        redisRolePojo.setMenuList(role.getMenuList());
        redisRolePojo.setName(role.getName());
        redisRolePojo.setUpdateTime(role.getUpdateTime());
        redisRolePojo.setCreatorId(role.getCreatorId());
        return redisRolePojo;
    }

    private List<Role> selectRedisData(List<String> roleListId) {
        List<Role> roles = new ArrayList<>();//存结果集合
        for (String roleId : roleListId) {//批量拼接 key
            String redisKey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + roleId;
            Object redisDepartment = serviceHelper.getRedisManager().get(redisKey);//redis中获取对应数据
            if (redisDepartment == null) {//不存在去数据库中查一下
                Query queryRole = Query.query(Criteria.where("_id").is(roleId));
                //获取对应的角色信息
                Role role = serviceHelper.getMongodbManager().findOne(queryRole, Role.class);
                if (role == null) {//数据库不存在存入缓存 避免重复对数据库添加压力
                    String roleRedisKey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + roleId;
                    serviceHelper.getRedisManager().set(roleRedisKey, RedisStorage.EMPTY_VALUE);
                    continue;
                }
                RedisRolePojo redisRolePojo = this.packaRedisRoleData(role);
                String rolekey = RedisKeyPrefixEnum.ROLE_ID_DATA.getCode() + "_" + redisRolePojo.get_id();
                String apiResultJson = JSONObject.toJSONString(redisRolePojo, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue);
                serviceHelper.getRedisManager().set(rolekey, apiResultJson);
                roles.add(role);
                continue;
            }
            if (RedisStorage.EMPTY_VALUE.equals(redisDepartment)) {//数据库中没此数据
                continue;
            }
            RedisRolePojo redisRolePojo = JSONObject.parseObject((String) redisDepartment, RedisRolePojo.class);//反序列化
            Role role = new Role();
            role.set_id(new ObjectId(redisRolePojo.get_id()));
            role.setName(redisRolePojo.getName());
            role.setAvailable(redisRolePojo.getAvailable());
            role.setCreateTime(redisRolePojo.getCreateTime());
            role.setMenuList(redisRolePojo.getMenuList());
            role.setUpdateTime(redisRolePojo.getUpdateTime());
            role.setCreatorId(redisRolePojo.getCreatorId());
            roles.add(role);
            continue;
        }
        return roles;
    }
}
